home *** CD-ROM | disk | FTP | other *** search
/ The World's Largest Collection of Windows Software / The World's Largest Collection of Windows Software - Disc 1.iso / connect / _j2 / wvnsc926 / wvlist.c < prev    next >
C/C++ Source or Header  |  1994-09-21  |  30KB  |  1,039 lines

  1.  
  2. /*
  3.  *
  4.  * $Id: wvlist.c 1.16 1994/09/16 01:03:53 jcooper Exp $
  5.  * $Log: wvlist.c $
  6.  * Revision 1.16  1994/09/16  01:03:53  jcooper
  7.  * New Determined flag to allow use of '-' instead of '0' in main group
  8.  * window for groups whose number of arts is unknown.
  9.  * plus general cleanup for 92.6
  10.  *
  11.  * Revision 1.15  1994/09/02  23:49:10  martin
  12.  * fixes for new hashtable stuff
  13.  *
  14.  * Revision 1.14  1994/08/26  22:14:43  dumoulin
  15.  * Attempts to fix horizontal scrolling in newgroups box
  16.  *
  17.  * Revision 1.13  1994/08/23  23:17:01  martin
  18.  * new group list box & new hash table mechanism
  19.  *
  20.  * Revision 1.12  1994/07/26  18:39:05  dumoulin
  21.  * Fixed WinVN bombing when number of new newsgroups exceeds
  22.  * 3000.  Now the new newsgroups dialog box doesn't get called.
  23.  *
  24.  * Revision 1.11  1994/06/08  21:01:45  gardnerd
  25.  * more scrolling changes...
  26.  *
  27.  * Revision 1.10  1994/05/27  01:29:29  rushing
  28.  * unnecessary winundoc.h
  29.  *
  30.  * Revision 1.9  1994/05/19  02:02:10  rushing
  31.  * changes for gensock & version 0.91
  32.  *
  33.  * Revision 1.8  1994/01/12  19:25:59  mrr
  34.  * mrr mods 4
  35.  *
  36.  * Revision 1.7  1993/12/08  01:28:38  rushing
  37.  * new version box and cr lf consistency
  38.  *
  39.  * Revision 1.6  1993/10/12  17:47:26  rushing
  40.  * make winvn grok servers that start article numbers at 0
  41.  *
  42.  * Revision 1.5  1993/08/18  21:51:13  rushing
  43.  * more 16-bit article number fixes
  44.  *
  45.  * Revision 1.4  1993/06/28  17:52:17  rushing
  46.  * fixed compiler warnings
  47.  *
  48.  * Revision 1.3  1993/06/24  17:07:05  dumoulin
  49.  * avoid max new groups > MAXGROUPS crash.
  50.  *
  51.  * Revision 1.2  1993/06/22  16:16:13  rushing
  52.  * raised MAXGROUPS (really MAXNEWGROUPS)
  53.  *
  54.  * Revision 1.1  1993/02/16  20:54:22  rushing
  55.  * Initial revision
  56.  *
  57.  *
  58.  */
  59.  
  60. /*-- WVLIST.C -- File containing functions to deal with the NNTP LIST
  61.  *   command, which lists all the newsgroups and their status.
  62.  *
  63.  *   Mark Riordan   25 October 1990
  64.  */
  65.  
  66. #include <windows.h>
  67. #include <windowsx.h>
  68. #include "wvglob.h"
  69. #include "winvn.h"
  70. #pragma hdrstop
  71. #include "dos.h"
  72. #include "primes.h"
  73.  
  74. #include <ctype.h>
  75.  
  76.  
  77. /* The size of the chunks of memory that make up the new group list data */
  78. #define CHUNKSIZE       64L*1024L
  79.  
  80. TypLine far *far * NetHashTable;
  81. HANDLE hNetHashTable;
  82.  
  83. HANDLE htohNewGroupLines;
  84. HANDLE far *lphNewGroupLines; /* array of handles to new group lines */
  85.  
  86. DWORD NewGroupDataSize; /* The size (in bytes) of NewGroupData */
  87. void huge *CurrentSpot; /* The pointer to the next available spot in NewGroupData */
  88. unsigned long   BytesSoFar;
  89.  
  90. #define MyGlobalUnlock(hWhat)  MRRGlobalUnlock(hWhat,__LINE__)
  91.  
  92. void MRRGlobalUnlock (HANDLE hWhat, WORD wLine);
  93.  
  94. void
  95. MRRGlobalUnlock (hWhat, wLine)
  96.      HANDLE hWhat;
  97.      WORD wLine;
  98. {
  99.   WORD LockCount;
  100.  
  101.   if (!(LockCount = (GMEM_LOCKCOUNT & GlobalFlags (hWhat))))
  102.     {
  103.     }
  104.   else
  105.     {
  106.       GlobalUnlock (hWhat);
  107.     }
  108.  
  109. }
  110.  
  111.  
  112. /*--- function StartList -----------------------------------------------
  113.  *
  114.  *  Initiate the process of sending a LIST command and using its
  115.  *  output to update our list of news groups.
  116.  */
  117.  
  118. void
  119. StartList ()
  120. {
  121.  
  122.  
  123.   CommState = ST_LIST_RESP;
  124.   CommBusy = TRUE;
  125.   PutCommLine ("LIST");
  126.   Initializing = INIT_SCANNING_NETDOC;
  127.   InvalidateRect (hWndConf, NULL, FALSE);
  128.   SendMessage (hWndConf, WM_PAINT, 0, 0L);
  129.  
  130.   /* Set up table of pointers to new group lines.*/
  131.   InitGroupTable();
  132.  
  133.   /* Initialize the hash table */
  134.   InitHashTable(LinesInRC);
  135.  
  136.   /* And set up the hash table for stuff from the newsrc */
  137.   HashNetGroups (&NetDoc, NetHashTable);
  138.   InvalidateRect (hWndConf, NULL, FALSE);
  139.   Initializing = INIT_GETTING_LIST;
  140.   SendMessage (hWndConf, WM_PAINT, 0, 0L);
  141.  
  142.  
  143. }
  144.  
  145. /*--- function InitHashTable -------------------------------------------
  146.  *
  147.  *  Allocate and zero the NetHashTable. The argument is the number of
  148.  *  elements to be hashed (NOT the number of elements in the hash table)
  149.  *
  150.  *  Exit:   hNetHashTable     is the handle
  151.  *          NetHashTable      points to the table
  152.  *                      HashTableSize     The number of elements in the hash table
  153.  */
  154. void
  155. InitHashTable(unsigned int Elements){
  156.  
  157.   /* First, figure out how large the hashtable should be.
  158.    * The basic algorithm used here is to double the size of what you're
  159.    * hashing, rounded up to the next nearest prime number
  160.    */
  161.   HashTableSize = (unsigned int)FindNextPrime((unsigned long)Elements * 2L);
  162.  
  163.   /* Set up hash table for group names */
  164.   hNetHashTable = GlobalAlloc (GMEM_MOVEABLE|GMEM_ZEROINIT,
  165.                            (unsigned long) HashTableSize *
  166.                            sizeof (TypLine far *));
  167.   NetHashTable = (TypLine far * far *) GlobalLock (hNetHashTable);
  168.  
  169. }
  170.  
  171.  
  172. /*--- function InitGroupTable -------------------------------------------
  173.  *
  174.  *  Allocate the first CHUNKSIZE bytes of memory for the
  175.  * new group lines.
  176.  *
  177.  *  Exit:   hNewGroupData    is the handle
  178.  *          NewGroupData     points to the table
  179.  *          nNewGroups        has been initialized to 0.
  180.  *                      NewGroupDataSize  is the current size of the NewGroupData
  181.  */
  182. void
  183. InitGroupTable (void)
  184. {
  185.   /* Allocate the first chunk */
  186.   NewGroupDataSize = CHUNKSIZE;
  187.   hNewGroupData = GlobalAlloc (GMEM_MOVEABLE, NewGroupDataSize);
  188.   NewGroupData = (void far *)GlobalLock(hNewGroupData);
  189.   CurrentSpot = NewGroupData;
  190.   BytesSoFar = 0;
  191.  
  192.   nNewGroups = 0;
  193. }
  194.  
  195.  
  196. /*--- function HashNetGroups ------------------------------------------
  197.  *
  198.  *  Enter all the groups in the Net document into the hash table.
  199.  *
  200.  *  Exit    All blocks in the document are locked.
  201.  *          HashTable   is (partially) filled with pointers to
  202.  *                      each of the lines in "Doc".
  203.  */
  204.  
  205. void
  206. HashNetGroups (Doc, HashTable)
  207.      TypDoc *Doc;
  208.      TypLine far *far * HashTable;
  209. {
  210.   TypBlock far *BlockPtr;
  211.   TypLine far *LinePtr;
  212.   unsigned int hashval;
  213.   HANDLE hBlock;
  214.   unsigned int Offset;
  215.   unsigned int TextOffset;
  216.   unsigned char far *textptr;
  217.   TypLineID MyLineID;
  218.  
  219.   /* Lock all blocks in the document */
  220.   hBlock = Doc->hFirstBlock;
  221.   do
  222.     {
  223.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  224.       hBlock = BlockPtr->hNextBlock;
  225.       }
  226.   while (hBlock);
  227.  
  228.  
  229.  
  230.   /* Now start at the beginning of the document, going through
  231.    * each line in the document, hashing its group name into the table.
  232.    */
  233.  
  234.   hBlock = Doc->hFirstBlock;
  235.   Offset = sizeof (TypBlock);
  236.   MyLineID = 0L;
  237.  
  238.   LockLine (hBlock, Offset, MyLineID, &BlockPtr, &LinePtr);
  239.  
  240.   TextOffset = Doc->OffsetToText;
  241.  
  242.   if (LinePtr->length != END_OF_BLOCK)
  243.     {
  244.       do
  245.    {
  246.      textptr = ((unsigned char far *) LinePtr) + TextOffset;
  247.      hashval = HashGroup (textptr);
  248.      while (HashTable[hashval])
  249.        {
  250.      hashval = (hashval + 1) % HashTableSize;
  251.        }
  252.      HashTable[hashval] = LinePtr;
  253.    }
  254.       while (NextLine (&BlockPtr, &LinePtr));
  255.     }
  256.   UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  257. }
  258.  
  259.  
  260. /*--- function ProcListLine ---------------------------------------------
  261.  *
  262.  *  Process a line received from the NNTP LIST command output.
  263.  *  Each line from the LIST command has the form:
  264.  *   <groupname> <highest_art_#> <lowest_art_#> {y|n|m}
  265.  */
  266.  
  267. void
  268. ProcListLine (ListLine)
  269.      unsigned char *ListLine;
  270. {
  271.   unsigned int hashval;
  272.   char far *textptr;
  273.   unsigned char *cptr, *restline;
  274.   char mygroupline[BLOCK_SIZE];
  275.   TypGroup *mygroup;
  276.   TypGroup far *netgroup;
  277.   long int ArtNum;
  278.  
  279.   if ((++RcvLineCount) % UPDATE_TITLE_FREQ == 0)
  280.     {
  281.       InvalidateRect (hWndConf, NULL, FALSE);
  282.       if (RcvLineCount % (UPDATE_TITLE_FREQ * 25) == 0)
  283.    {
  284.      UpdateWindow (hWndConf);
  285.    }
  286.     }
  287.  
  288.   /* Replace the first blank in the input line with a zero. */
  289.   for (cptr = ListLine; *cptr && *cptr != ' '; cptr++);
  290.   *cptr = '\0';
  291.   restline = cptr + 1;     /* points to highest art # */
  292.  
  293.   hashval = HashGroup (ListLine);
  294. checkhash:;
  295.   if (!NetHashTable[hashval])
  296.     {
  297.       /* This is a new group.
  298.        * Create a Group line from the information in this line.
  299.        */
  300.  
  301.     CrackGroupLine (ListLine, (TypLine *) (mygroupline));
  302.     mygroup = (TypGroup *) (mygroupline + sizeof (TypLine));
  303.     GetNum (&restline, &(mygroup->ServerLast));
  304.     GetNum (&restline, &(mygroup->ServerFirst));
  305.     mygroup->ServerEstNum = mygroup->ServerLast - mygroup->ServerFirst+ 1;
  306.     mygroup->HighestPrevSeen = 0;
  307.     mygroup->nRanges = 0;
  308.     mygroup->Determined = TRUE;
  309.  
  310.     AddGroupToTable (mygroupline);
  311.     }
  312.   else
  313.     {
  314.       textptr = ((char far *) NetHashTable[hashval] + sizeof (TypLine) + sizeof (TypGroup));
  315.       if (lstrcmp (textptr, ListLine))
  316.    {
  317.      hashval = (hashval + 1) % HashTableSize;
  318.      goto checkhash;
  319.    }
  320.       else
  321.    {
  322.      /* This group is already present in NetDoc.
  323.        * Update the ServerFirst and ServerLast fields.
  324.        */
  325.      netgroup = (TypGroup far *) ((char far *) NetHashTable[hashval] + sizeof (TypLine));
  326.      GetNum (&restline, &ArtNum);
  327.      netgroup->ServerLast = ArtNum;
  328.      GetNum (&restline, &ArtNum);
  329.      netgroup->ServerFirst = ArtNum;
  330.      netgroup->ServerEstNum = (netgroup->ServerLast - netgroup->ServerFirst + 1);
  331.      netgroup->Determined = TRUE;
  332.  
  333.    }
  334.     }
  335.  
  336. }
  337.  
  338. /*--- function AddGroupToTable ----------------------------------------
  339.  *
  340.  *  Add a group line, formatted for eventual inclusion in NetDoc,
  341.  *  to NewGroupTable.
  342.  *
  343.  *  Entry:  GroupLine      is the line to add to the table.
  344.  *
  345.  *  Exit:   NewGroupTable  contains the line
  346.  *          nNewGroups     has been incremented.
  347.  */
  348. void
  349. AddGroupToTable (char far * GroupLine)
  350. {
  351.   char huge *NextPtr;
  352.   char huge *EndPtr;
  353.  
  354.   NextPtr = (char huge *)CurrentSpot + ((TypLine far *) GroupLine)->length;
  355.   EndPtr = (char huge *)NewGroupData + NewGroupDataSize;
  356.  
  357.   /* Check to see if the current data will put us over the edge */
  358.   if (NextPtr > EndPtr){
  359.      GlobalUnlock(hNewGroupData);
  360.      NewGroupDataSize += CHUNKSIZE;
  361.      hNewGroupData = GlobalReAlloc(hNewGroupData, NewGroupDataSize,
  362.                        GMEM_MOVEABLE);
  363.      NewGroupData = GlobalLock(hNewGroupData);
  364.      CurrentSpot = (char huge *)NewGroupData + BytesSoFar;
  365.      NextPtr = (char huge *)CurrentSpot + ((TypLine far *) GroupLine)->length;
  366.   }
  367.  
  368.   /* Create a copy of this line in far memory
  369.    */
  370.   MoveBytes((char far *)GroupLine, CurrentSpot,
  371.                     ((TypLine far *) GroupLine)->length);
  372.   nNewGroups++;
  373.  
  374.   BytesSoFar += ((TypLine huge *) CurrentSpot)->length;
  375.   (char huge *)CurrentSpot = NextPtr;
  376.  
  377. }
  378.  
  379. /*--- function HashGroup -------------------------------------------------
  380.  *
  381.  *  Hash a string into an unsigned integer.
  382.  *
  383.  *  This hash function is designed based on information from a
  384.  *  handout for a UC Berkeley class CS 60C, Spring 1990, Clancy/
  385.  *  Harrison.  I picked up the handout while wandering around on
  386.  *  Berkeley's campus in May 1990.  The handout in turn is based
  387.  *  on the McKenzie, et al., article "Selecting a Hashing Algorithm"
  388.  *  in Software Practice and Experience, Vol 20, no 2, Feb 1990.
  389.  *  The algorithm is similar to that used in the AT&T C++ compiler.
  390.  *  /mrr
  391.  */
  392.  
  393. unsigned int
  394. HashGroup (gname)
  395.      unsigned char huge *gname;
  396. {
  397.   long unsigned int sum = 0;
  398.   unsigned int hash;
  399.  
  400.   for (; *gname; gname++)
  401.     {
  402.       sum = (sum << 1) + *gname;
  403.     }
  404.   hash = (unsigned int) (sum % HashTableSize);
  405.   return (hash);
  406. }
  407.  
  408. /*--- function BuildPtrList ------------------------------------------
  409.  *
  410.  *  Build both the array of pointers into the new group data, as well
  411.  *  as build the hashtable.
  412.  */
  413. void
  414. BuildPtrList ()
  415. {
  416.   int i;
  417.  
  418.   /* Build the pointer array after the fact */
  419.   hNewGroupTable = GlobalAlloc (GMEM_MOVEABLE, (long) nNewGroups * sizeof (TypLine huge *));
  420.   NewGroupTable = (void huge * huge *) GlobalLock (hNewGroupTable);
  421.   CurrentSpot = NewGroupData;
  422.  
  423.   for(i=0 ; i < nNewGroups ; i++){
  424.     NewGroupTable[i]=CurrentSpot;
  425.     (char huge *)CurrentSpot += ((TypLine huge *) CurrentSpot)->length;
  426.   }
  427. }
  428.  
  429. /*--- function BuildHashTable ------------------------------------------
  430.  *
  431.  *  Build the hashtable from the NewGroupTable.
  432.  */
  433. void
  434. BuildHashTable ()
  435. {
  436.   unsigned int hashval;
  437.   char huge *textptr;
  438.   int i;
  439.  
  440.   for(i=0 ; i < nNewGroups ; i++){
  441.     /* Plus fill in the hash table */
  442.     textptr = (char huge *)NewGroupTable[i] + sizeof(TypLine)
  443.         + sizeof(TypGroup);
  444.     hashval = HashGroup (textptr);
  445.     while (NetHashTable[hashval])
  446.     {
  447.        hashval = (hashval + 1) % HashTableSize;
  448.     }
  449.     NetHashTable[hashval] = NewGroupTable[i];
  450.   }
  451.  
  452. }
  453.  
  454.  
  455. /*--- function ProcEndList -------------------------------------------
  456.  *
  457.  *  Do the final processing when we have reached the end of the
  458.  *  list of newsgroups sent us via the LIST command.
  459.  */
  460.  
  461. void
  462. ProcEndList ()
  463. {
  464.   DLGPROC lpfnWinVnGroupListDlg;
  465.   WORD LockCount;
  466.  
  467.   /* Well, we need a new and different hash table now, so close out the
  468.      old one */
  469.   LockCount = GMEM_LOCKCOUNT & GlobalFlags (hNetHashTable);
  470.   MyGlobalUnlock (hNetHashTable);
  471.   GlobalFree (hNetHashTable);
  472.  
  473.   InitHashTable (nNewGroups);
  474.   BuildPtrList ();
  475.   BuildHashTable ();
  476.  
  477.   ShellSort (NewGroupTable, nNewGroups, sizeof (void huge *), GroupCompare);
  478.  
  479.   lpfnWinVnGroupListDlg = (DLGPROC)MakeProcInstance ((FARPROC)WinVnGroupListDlg, hInst);
  480.  
  481.   /* Display dialog box of new groups.  */
  482.   if (nNewGroups && DialogBox (hInst, "WinVnGroupList", hWndConf, lpfnWinVnGroupListDlg))
  483.   {
  484.     /* The user has clicked OK, so add all the new groups to the
  485.      * Net document.  Subscribed groups go at the end of the Subscribed
  486.      * section at the top of the doc.
  487.      * Unsubscribed groups go in the section below, in alphabetical order.
  488.      */
  489.     MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
  490.     NetDoc.LongestLine = 0;
  491.     SetGroupActiveLines();
  492.   }
  493.  
  494.   /* Unlock and/or free memory in NetDoc and NewGroupTable.  */
  495.  
  496.   CleanUpGroupTable ();
  497.  
  498.   /* Unlock and free the hash table.   */
  499.  
  500.   LockCount = GMEM_LOCKCOUNT & GlobalFlags (hNetHashTable);
  501.   MyGlobalUnlock (hNetHashTable);
  502.   GlobalFree (hNetHashTable);
  503.  
  504.   InvalidateRect (hWndConf, NULL, FALSE);
  505.   SetNetDocTitle ();
  506. }
  507.  
  508. /*--- function GroupCompare --------------------------------------------
  509.  *
  510.  *  Compare two group lines alphabetically by group name.
  511.  */
  512. int
  513. GroupCompare (TypLine const huge * huge * g1,
  514.               TypLine const huge * huge * g2)
  515. {
  516.   char huge *gch1, huge * gch2;
  517.  
  518.   gch1 = (char huge *) *g1 + sizeof (TypLine) + sizeof (TypGroup);
  519.   gch2 = (char huge *) *g2 + sizeof (TypLine) + sizeof (TypGroup);
  520.  
  521.   return (lstrcmp (gch1, gch2));
  522. }
  523.  
  524.  
  525. /*-- function WinVnGroupListDlg ---------------------------------------
  526.  *
  527.  *  Dialog function to handle selection of new newsgroups to
  528.  *  subscribe to.  (I know, don't end a sentence with "to".)
  529.  */
  530.  
  531. BOOL FAR PASCAL
  532. WinVnGroupListDlg (hDlg, iMessage, wParam, lParam)
  533.      HWND hDlg;
  534.      unsigned iMessage;
  535.      WPARAM wParam;
  536.      LPARAM lParam;
  537. {
  538.   int j;
  539.   LRESULT nItem;
  540.   char TmpBuf[80];
  541.   char far *CurName;
  542.   char huge * cptr;
  543.   TypHier far *CurHier;
  544.   static HANDLE hStartHier,hCurHier;
  545.   unsigned int hashval;
  546.   TypGroup huge *group;
  547.   HWND hDlgHierList;       /* Handle to child hierarchy list box window. */
  548.   HWND hDlgSubList;
  549.   HWND hDlgUnSubList;
  550.   switch (iMessage)
  551.     {
  552.  
  553.     case WM_INITDIALOG:
  554.       /* First build the list of top-level hierarchies */
  555.       CurHier =(TypHier *)NULL;
  556.       hStartHier = hCurHier = (HANDLE)NULL;
  557.       for (j = 0; j < nNewGroups; j++)
  558.       {
  559.     cptr = 0;
  560.     cptr = (NewGroupTable[j]);
  561.     cptr += sizeof (TypLine) + sizeof (TypGroup);
  562.     if((CurHier == NULL) ||
  563.        (strnicmp(CurName,cptr,strcspn(cptr,"."))
  564.         != 0)){
  565.        HANDLE hTmpHandle;
  566.        int NameLen;
  567.        /* Need a new link */
  568.        hTmpHandle = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,
  569.                                 sizeof(TypHier));
  570.        if(CurHier){
  571.         CurHier->hNext = hTmpHandle;
  572.         GlobalUnlock(CurHier->hName);
  573.         GlobalUnlock(hCurHier);
  574.         hCurHier = hTmpHandle;
  575.         CurHier = (TypHier far *)GlobalLock(hCurHier);
  576.        } else {
  577.         hCurHier = hStartHier = hTmpHandle;
  578.         CurHier = (TypHier far *)GlobalLock(hCurHier);
  579.        }
  580.        /* And now for the actual hierarchy name */
  581.        NameLen = strcspn(cptr,".");
  582.        CurHier->hName = GlobalAlloc(GMEM_MOVEABLE,NameLen+1);
  583.        CurName = GlobalLock(CurHier->hName);
  584.        strncpy(CurName,cptr,NameLen);
  585.        CurName[NameLen] = '\0';
  586.            /* And the start index */
  587.            CurHier->Start = j;
  588.     }
  589.     /* Increment the number of groups _every_ time */
  590.     CurHier->NumGroups++;
  591.       }
  592.       GlobalUnlock(CurHier->hName);
  593.       GlobalUnlock(hCurHier);
  594.  
  595.       /* Now, load the HIERARCY box */
  596.       hDlgHierList = GetDlgItem (hDlg, IDD_HIERARCHY_LISTBOX);
  597.       SendMessage (hDlgHierList, WM_SETREDRAW, FALSE, 0L);
  598.       hCurHier = hStartHier;
  599.       while(hCurHier){
  600.     HANDLE hTmpHandle;
  601.     CurHier = (TypHier far *)GlobalLock(hCurHier);
  602.     CurName = GlobalLock(CurHier->hName);
  603.     SendMessage (hDlgHierList, LB_ADDSTRING, 0, (LPARAM)CurName);
  604.     hTmpHandle = CurHier->hNext;
  605.         GlobalUnlock(CurHier->hName);
  606.     GlobalUnlock(hCurHier);
  607.     hCurHier = hTmpHandle;
  608.       }
  609.       SendMessage (hDlgHierList, WM_SETREDRAW, TRUE, 0L);
  610.  
  611.       /* And select the 0th item to kick things off */
  612.       SendMessage (hDlgHierList, LB_SETCURSEL, 0 , 0);
  613. #ifdef WIN32
  614.       SendMessage (hDlg,WM_COMMAND,(WPARAM)MAKEWPARAM(IDD_HIERARCHY_LISTBOX,
  615.            LBN_SELCHANGE),(LPARAM)GetDlgItem(hDlg,IDD_HIERARCHY_LISTBOX));
  616. #else
  617.       SendMessage (hDlg,WM_COMMAND,IDD_HIERARCHY_LISTBOX,
  618.            (LPARAM)MAKELPARAM(GetDlgItem(hDlg,IDD_HIERARCHY_LISTBOX),
  619.                               LBN_SELCHANGE));
  620. #endif /* WIN32 */
  621.  
  622.       return TRUE;
  623.       break;
  624.  
  625.     case WM_COMMAND:
  626.       switch (LOWORD(wParam))
  627.       {
  628.       case IDOK:
  629.     EndDialog (hDlg, TRUE);
  630.     break;
  631.       case IDCANCEL:
  632.     EndDialog (hDlg, FALSE);
  633.     break;
  634.       case IDD_SUBSCRIBED_GROUP_LISTBOX:
  635. #ifdef WIN32
  636.         switch(HIWORD(wParam))
  637. #else
  638.         switch(HIWORD(lParam))
  639. #endif /* WIN32 */
  640.         {
  641.         case LBN_DBLCLK:
  642.           /* switch to unsubscribed */
  643. #ifdef WIN32
  644.       nItem = SendMessage((HWND)lParam,LB_GETCURSEL, 0 , 0);
  645.       SendMessage((HWND)lParam,LB_GETTEXT,(WPARAM)nItem,
  646.               (LPARAM) TmpBuf);
  647.       SendMessage((HWND)lParam,LB_DELETESTRING,(WPARAM)nItem,
  648.               (LPARAM) 0);
  649.  
  650. #else
  651.       nItem = SendMessage((HWND)LOWORD(lParam),LB_GETCURSEL, 0 , 0);
  652.       SendMessage((HWND)LOWORD(lParam),LB_GETTEXT,(WPARAM)nItem,
  653.               (LPARAM) TmpBuf);
  654.       SendMessage((HWND)LOWORD(lParam),LB_DELETESTRING,(WPARAM)nItem,
  655.               (LPARAM) 0);
  656. #endif /* WIN32 */
  657.           /* Now pull the stucture out of the hash table */
  658.       hashval = HashGroup (TmpBuf);
  659.           while (strcmp(
  660.        (char huge *)NetHashTable[hashval]+sizeof(TypLine)+sizeof(TypGroup),
  661.        TmpBuf) != 0)
  662.       {
  663.          hashval = (hashval + 1) % HashTableSize;
  664.       }
  665.       group = (TypGroup huge *)
  666.            ((char huge *)NetHashTable[hashval] + sizeof(TypLine));
  667.       group->Subscribed = 0;
  668.  
  669.       SendMessage(GetDlgItem(hDlg,IDD_UNSUBSCRIBED_GROUP_LISTBOX),
  670.                   LB_ADDSTRING, 0, (LPARAM)TmpBuf);
  671.       break;
  672.     default:
  673.         return FALSE;
  674.  
  675.     }
  676.     break;
  677.       case IDD_UNSUBSCRIBED_GROUP_LISTBOX:
  678. #ifdef WIN32
  679.         switch(HIWORD(wParam))
  680. #else
  681.         switch(HIWORD(lParam))
  682. #endif /* WIN32 */
  683.         {
  684.         case LBN_DBLCLK:
  685.           /* switch to subscribed */
  686. #ifdef WIN32
  687.       nItem = SendMessage((HWND)lParam,LB_GETCURSEL, 0 , 0);
  688.       SendMessage((HWND)lParam,LB_GETTEXT,(WPARAM)nItem,
  689.               (LPARAM) TmpBuf);
  690.       SendMessage((HWND)lParam,LB_DELETESTRING,(WPARAM)nItem,
  691.               (LPARAM) 0);
  692. #else
  693.       nItem = SendMessage((HWND)LOWORD(lParam),LB_GETCURSEL, 0 , 0);
  694.       SendMessage((HWND)LOWORD(lParam),LB_GETTEXT,(WPARAM)nItem,
  695.               (LPARAM) TmpBuf);
  696.       SendMessage((HWND)LOWORD(lParam),LB_DELETESTRING,(WPARAM)nItem,
  697.               (LPARAM) 0);
  698. #endif /* WIN32 */
  699.       /* Now pull the stucture out of the hash table */
  700.       hashval = HashGroup (TmpBuf);
  701.       while (strcmp((char huge *)NetHashTable[hashval]+
  702.                        sizeof(TypLine)+sizeof(TypGroup),
  703.                        TmpBuf) != 0)
  704.       {
  705.          hashval = (hashval + 1) % HashTableSize;
  706.       }
  707.       group = (TypGroup huge *)
  708.            ((char huge *)NetHashTable[hashval] + sizeof(TypLine));
  709.       group->Subscribed = 1;
  710.  
  711.       SendMessage(GetDlgItem(hDlg,IDD_SUBSCRIBED_GROUP_LISTBOX),
  712.                   LB_ADDSTRING,0, (LPARAM)TmpBuf);
  713.       break;
  714.     default:
  715.         return FALSE;
  716.  
  717.     }
  718.     break;
  719.       case IDD_HIERARCHY_LISTBOX:
  720. #ifdef WIN32
  721.         switch(HIWORD(wParam))
  722. #else
  723.         switch(HIWORD(lParam))
  724. #endif /* WIN32 */
  725.         {
  726.         case LBN_SELCHANGE:
  727.           /* Find the info for this hierarchy */
  728. #ifdef WIN32
  729.       nItem = SendMessage((HWND)lParam,LB_GETCURSEL, 0 , 0);
  730.       SendMessage((HWND)lParam,LB_GETTEXT,(WPARAM)nItem,
  731.               (LPARAM) TmpBuf);
  732. #else
  733.       nItem = SendMessage((HWND)LOWORD(lParam),LB_GETCURSEL, 0 , 0);
  734.       SendMessage((HWND)LOWORD(lParam),LB_GETTEXT,(WPARAM)nItem,
  735.               (LPARAM) TmpBuf);
  736. #endif /* WIN32 */
  737.       hCurHier = hStartHier;
  738.       while(hCurHier){
  739.         HANDLE hTmpHandle;
  740.         CurHier = (TypHier far *)GlobalLock(hCurHier);
  741.         CurName = GlobalLock(CurHier->hName);
  742.             GlobalUnlock(CurHier->hName);
  743.             if(stricmp(CurName,TmpBuf) == 0)
  744.                 break;
  745.         hTmpHandle = CurHier->hNext;
  746.             GlobalUnlock(hCurHier);
  747.         hCurHier = hTmpHandle;
  748.       }
  749.           /* clear the existing listboxes */
  750. #ifdef WIN32
  751.           SendMessage ((HWND)lParam, WM_SETREDRAW, FALSE, 0L);
  752. #else
  753.           SendMessage ((HWND)LOWORD(lParam), WM_SETREDRAW, FALSE, 0L);
  754. #endif /* WIN32 */
  755.           hDlgSubList = GetDlgItem (hDlg, IDD_SUBSCRIBED_GROUP_LISTBOX);
  756.           hDlgUnSubList = GetDlgItem (hDlg, IDD_UNSUBSCRIBED_GROUP_LISTBOX);
  757.           SendMessage (hDlgUnSubList, LB_RESETCONTENT,0,0);
  758.           SendMessage (hDlgSubList, LB_RESETCONTENT,0,0);
  759.           /* load the subscribed/unsubscribed boxes */
  760.           for (j=CurHier->Start; j<(CurHier->Start + CurHier->NumGroups); j++){
  761.             TypGroup huge *group;
  762.             group = (TypGroup huge *)
  763.                     ((char huge *)NewGroupTable[j] + sizeof(TypLine));
  764.         if (group->Subscribed)
  765.           SendMessage (hDlgSubList, LB_ADDSTRING, 0,
  766.                (LPARAM)((char huge*)group + sizeof(TypGroup)));
  767.         else
  768.           SendMessage (hDlgUnSubList, LB_ADDSTRING, 0,
  769.                (LPARAM)((char huge *)group + sizeof(TypGroup)));
  770.       }
  771.  
  772.     //  Horizontal scrolling doesn't work.   These are all attempts to
  773.     //  fix it that don't work.  However, it isn't worth holding up
  774.     //  the release of .92-3 for this one....JD 8/26/94
  775.  
  776.  
  777.     //  SendDlgItemMessage(GetDlgItem(hDlg,IDD_SUBSCRIBED_GROUP_LISTBOX), IDD_SUBSCRIBED_GROUP_LISTBOX,       // JD 8/25/94
  778.     //                         LB_SETHORIZONTALEXTENT,(WPARAM)200,(LPARAM)0);
  779.     //  SetScrollRange(GetDlgItem(hDlg,IDD_SUBSCRIBED_GROUP_LISTBOX),
  780.     //             SB_CTL, (int) 0, (int) 1200, TRUE);   // JD 8/26/94
  781.     //
  782.     //  SetScrollRange(GetDlgItem(hDlg,IDD_UNSUBSCRIBED_GROUP_LISTBOX),
  783.     //             SB_HORZ, (int) 0, (int) 20, TRUE);   // JD 8/26/94
  784.  
  785. #ifdef WIN32
  786.       SendMessage ((HWND)lParam, WM_SETREDRAW, TRUE, 0L);
  787. #else
  788.       SendMessage ((HWND)LOWORD(lParam), WM_SETREDRAW, TRUE, 0L);
  789. #endif /* WIN32 */
  790.           GlobalUnlock(hCurHier);
  791.           break;
  792.     default:
  793.       return FALSE;
  794.     }
  795.     break;
  796.       default:
  797.     return FALSE;
  798.     }
  799.     break;
  800.  
  801.     case WM_DESTROY:
  802.       /* Ok, now we tear down the hierarchy structure */
  803.       hCurHier = hStartHier;
  804.       while(hCurHier){
  805.         HANDLE  hTmpHandle;
  806.         CurHier = (TypHier far *)GlobalLock(hCurHier);
  807.         GlobalFree(CurHier->hName);
  808.         hTmpHandle = CurHier->hNext;
  809.         GlobalUnlock(hCurHier);
  810.         GlobalFree(hCurHier);
  811.         hCurHier = hTmpHandle;
  812.       }
  813.     break;
  814.  
  815.     default:
  816.       return FALSE;
  817.       break;
  818.     }
  819.   return TRUE;
  820. }
  821.  
  822. /*--- function PositionEndSubscribed ----------------------------------
  823.  *
  824.  *  Position a pointer to the end of the subscribed section at the
  825.  *  beginning of the net document.
  826.  *
  827.  *  Entry   None
  828.  *
  829.  *  Exit    BlockPtr and LinePtr  point to the place in NetDoc just
  830.  *            beyond the last subscribed group.  We assume that
  831.  *            all subscribed groups go at the beginning of the
  832.  *            document.
  833.  */
  834. void
  835. PositionEndSubscribed (TypBlock far ** BlockPtr, TypLine far ** LinePtr)
  836. {
  837.   BOOL advance;
  838.   TypGroup far *group;
  839.  
  840.   TopOfDoc (&NetDoc, BlockPtr, LinePtr);
  841.   advance = TRUE;
  842.   do
  843.     {
  844.       group = (TypGroup far *) ((char far *) *LinePtr + sizeof (TypLine));
  845.       if (group->Subscribed)
  846.    {
  847.      advance = NextLine (BlockPtr, LinePtr);
  848.    }
  849.       else
  850.    {
  851.      advance = FALSE;
  852.    }
  853.     }
  854.   while (advance);
  855. }
  856.  
  857. /*--- function MergeGroups ----------------------------------------
  858.  *
  859.  *  Merge a list of groups into NetDoc.
  860.  *
  861.  *  Entry:  NewGroupTable  is an array of pointers to TypGroup structures
  862.  *                         of groups to be merged into NetDoc.
  863.  *          hNewGroupTable is the handle to the above.
  864.  *          nNewGroups     is the number of groups in the table.
  865.  *          WhereSubscribed   indicates where new subscribed groups
  866.  *                         should be added.
  867.  *                         ADD_SUBSCRIBED_END_OF_SUB indicates that
  868.  *                           they should be added at the end of the subscribed
  869.  *                           list, before the unsubscribed groups.
  870.  *                         ADD_SUBSCRIBED_TOP_OF_DOC indicates that they
  871.  *                           should be added at the top of the document.
  872.  *  Exit:   The groups in the table have been added to NetDoc, and
  873.  *            the entries in GroupTable have been freed from memory.
  874.  *            Also, GroupTable itself has been freed.
  875.  */
  876. void
  877. MergeGroups (int WhereSubscribed)
  878. {
  879.   TypBlock far *BlockPtr;
  880.   TypLine far *LinePtr;
  881.   TypGroup far *group;
  882.   char far *netcptr;
  883.   char far *grpcptr;
  884.   void far *AllocPtr;
  885.   HANDLE hLine;
  886.   unsigned int Offset;
  887.   TypLineID MyLineID;
  888.   char myline[BLOCK_SIZE];
  889.   int j, advance, at_end = 0;
  890.  
  891.   switch (WhereSubscribed)
  892.     {
  893.     case ADD_SUBSCRIBED_END_OF_SUB:
  894.       PositionEndSubscribed (&BlockPtr, &LinePtr);
  895.       break;
  896.     case ADD_SUBSCRIBED_TOP_OF_DOC:
  897.       TopOfDoc (&NetDoc, &BlockPtr, &LinePtr);
  898.       break;
  899.     }
  900.  
  901.   /* BlockPtr and LinePtr point to the
  902.    * place to add new subscribed groups.
  903.    * Loop through the new groups; for subscribed groups, add
  904.    * them to NetDoc at this point.
  905.    * For each subscribed group, unlock and free the corresponding
  906.    * line pointed to by NewGroupTable.  Set the table entry
  907.    * to 0 to indicate that this group has been dealt with.
  908.    */
  909.  
  910.   for (j = 0; j < nNewGroups; j++)
  911.     {
  912.       AllocPtr = (char far *) NewGroupTable[j];
  913.       group = (TypGroup far *) ((char far *) AllocPtr + sizeof (TypLine));
  914.       if (group->Subscribed)
  915.    {
  916.      /* This group has been selected and should be subscribed to.
  917.        */
  918.      MoveBytes (AllocPtr, myline, ((TypLine far *) AllocPtr)->length);
  919.      LinePtr->active = TRUE;
  920.      AddLine ((TypLine *) myline, &BlockPtr, &LinePtr);
  921.      NetDoc.ActiveLines++;
  922.      NewGroupTable[j] = (void far *) 0;
  923.    }
  924.     }
  925.  
  926.   PositionEndSubscribed (&BlockPtr, &LinePtr);
  927.  
  928.   /* Now take a second pass through NewGroupTable, for the
  929.    * unsubscribed groups.  If NewGroupTable[j] is non-zero, then
  930.    * that group should be entered into the second, unsubscribed
  931.    * section of NetDoc, merged in in alphabetical order.
  932.    *
  933.    * BlockPtr and LinePtr point to the first unsubscribed group.
  934.    */
  935.  
  936.   for (j = 0; j < nNewGroups; j++)
  937.     {
  938.       if (NewGroupTable[j])
  939.    {
  940.      /* Search for the right place to add this line. */
  941.  
  942.      AllocPtr = (char far *) NewGroupTable[j];
  943.      grpcptr = ((char far *) AllocPtr) + sizeof (TypLine) + sizeof (TypGroup);
  944.      advance = TRUE;
  945.  
  946.      if (!at_end) {
  947.      do
  948.        {
  949.      netcptr = ((char far *) LinePtr) + sizeof (TypLine) + sizeof (TypGroup);
  950.      if (lstrcmp (grpcptr, netcptr) < 0)
  951.       {
  952.     advance = FALSE;
  953.       }
  954.      else
  955.       {
  956.     advance = NextLine (&BlockPtr, &LinePtr);
  957.     if (!advance) at_end = 1; /* possible bug, getting bad netcptr (smr) */
  958.       }
  959.        }
  960.      while (advance);
  961.      }
  962.  
  963.      /* Now add the new group at this point */
  964.      MoveBytes (AllocPtr, myline, ((TypLine far *) AllocPtr)->length);
  965.      LinePtr->active = ShowUnsubscribed;
  966.      AddLine ((TypLine *) myline, &BlockPtr, &LinePtr);
  967.  
  968.    }
  969.     }
  970.  
  971.   UnlockLine (BlockPtr, LinePtr, &hLine, &Offset, &MyLineID);
  972. }
  973.  
  974. /*--- function CleanUpGroupTable ------------------------------------
  975.  *
  976.  *  Clean up after doing processing to add or move groups in NetDoc.
  977.  */
  978. void
  979. CleanUpGroupTable ()
  980. {
  981.   HANDLE hBlock, hBlockNext;
  982.   TypBlock far *BlockPtr;
  983.  
  984.   /* Unlock all blocks in the NetDoc document.
  985.    */
  986.  
  987.   hBlock = NetDoc.hFirstBlock;
  988.   do
  989.     {
  990.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  991.       hBlockNext = BlockPtr->hNextBlock;
  992.  
  993.       MyGlobalUnlock (hBlock);
  994.       MyGlobalUnlock (hBlock);
  995.       hBlock = hBlockNext;
  996.     }
  997.   while (hBlock);
  998.  
  999.   /* Unlock and free the NewGroupTable and NewGroupData.  */
  1000.  
  1001.   GlobalUnlock (hNewGroupData);
  1002.   GlobalFree (hNewGroupData);
  1003.   MyGlobalUnlock (hNewGroupTable);
  1004.   GlobalFree (hNewGroupTable);
  1005.  
  1006. }
  1007.  
  1008. /*--- function FindNextPrime ------------------------------------
  1009.  *
  1010.  *  Find the smallest prime that is larger than the given argument.
  1011.  *  Extreemly handy for those annoying dynamic hash tables.
  1012.  */
  1013. unsigned long
  1014. FindNextPrime(unsigned long OrigVal)
  1015. {
  1016.  
  1017.     int MaxPrimeIndex = (sizeof(prime)/sizeof(prime[0]))-1;
  1018.     int     IndexA = 0;
  1019.     int IndexB = MaxPrimeIndex;
  1020.     int TmpIndex;
  1021.  
  1022.     /* If we're larger than the max prime we know of, just return it */
  1023.     if(OrigVal >= prime[MaxPrimeIndex])
  1024.         return(prime[MaxPrimeIndex]);
  1025.  
  1026.     /* We do this by doing a binary search of the prime array */
  1027.     do {
  1028.         TmpIndex = IndexA +((IndexB-IndexA)/2);
  1029.         if( OrigVal == prime[TmpIndex])
  1030.             return(prime[TmpIndex+1]);
  1031.         if( OrigVal < prime[TmpIndex])
  1032.             IndexB = TmpIndex;
  1033.         else
  1034.             IndexA = TmpIndex;
  1035.     } while ( (IndexB-IndexA) > 1);
  1036.  
  1037.     return(prime[IndexA+1]);
  1038. }
  1039.